{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "import warnings\n",
    "warnings.filterwarnings('ignore')\n",
    "import os\n",
    "import sys\n",
    "sys.path.append('./common')\n",
    "from processing import list2dict, SpliceData, generate_data, read_ratings\n",
    "from metric import *\n",
    "from recommend import *\n",
    "from evaluate import my_test, Mythead\n",
    "import numpy as np\n",
    "import time\n",
    "from collections import Counter\n",
    "import threading\n",
    "import pandas as pd"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 1. 构造用户物品数据\n",
    "\n",
    "一般来说，不同的数据集包含不同的行为，\n",
    "目前比较有代表性的数据集有下面几个。\n",
    "- **无上下文信息的隐性反馈数据集**  每一条行为记录仅仅包含用户ID和物品ID。\n",
    "- **无上下文信息的显性反馈数据集**  每一条记录包含用户ID、物品ID和用户对物品的评分。\n",
    "- **有上下文信息的隐性反馈数据集**  每一条记录包含用户ID、物品ID和用户对物品产生行为的时间戳。\n",
    "- **有上下文信息的显性反馈数据集**  每一条记录包含用户ID、物品ID、用户对物品的评分和评分行为发生的时间戳。\n",
    "\n",
    "本章使用的数据集基本都是第一种数据集，即无上下文信息的隐性反馈数据集。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "另外我们也可以自行构造随机数据，因为商店中的商品被购买的概率不是一样的，简单的我们可以以2-8分布来简单地构造数据。举例来说：\n",
    "1. 假定物品数量总数为N；\n",
    "2. 假定`20%`的物品出售的数量占了总体的`80%`；\n",
    "3. 那么根据以下公式，可以得出20%的物品相比较其他物品来说出售概率是其`16`倍。 \n",
    "\n",
    "$$\n",
    "nps = \\frac{(1 - item_p)}{item_p} * \\frac{p}{1 - p}\n",
    "$$\n",
    "\n",
    "   其中**item_p**为畅销产品的占比，**p**为畅销产品的出售占比。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 1.1 随机生成数据代码"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 1.2 检查数据分布\n",
    "\n",
    "我们将生成的数据转化为数据框，并通过统计不同物品的出售数量，查看占比是否符合预期。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "20.0%的物品出现概率是其他物品的16.00倍\n",
      "(10000, 2)\n"
     ]
    }
   ],
   "source": [
    "# 构造数据\n",
    "data = generate_data(user_nums = 200, item_nums = 400, N = 10000, item_p = 0.2, p = 0.8, seed = 1234)\n",
    "print(data.shape)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>userid</th>\n",
       "      <th>itemid</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>47</td>\n",
       "      <td>98</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>38</td>\n",
       "      <td>57</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>53</td>\n",
       "      <td>34</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>116</td>\n",
       "      <td>73</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>152</td>\n",
       "      <td>0</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "   userid  itemid\n",
       "0      47      98\n",
       "1      38      57\n",
       "2      53      34\n",
       "3     116      73\n",
       "4     152       0"
      ]
     },
     "execution_count": 3,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "import pandas as pd\n",
    "data_new = pd.DataFrame(data, columns = ['userid', 'itemid'])\n",
    "data_new.head()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "我们可以直观地看到大多数不同的itemid出售数量相差会很大。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "0.8021\n"
     ]
    }
   ],
   "source": [
    "# 统计20%的物品的出售数量占比\n",
    "from collections import Counter\n",
    "\n",
    "cnt = Counter(data_new.itemid)\n",
    "res1 = 0  # 存储前20%的物品出售数量\n",
    "for i in cnt.keys():\n",
    "    res1 += cnt[i] if i < 400 * 0.2 else 0  # 即前20%的物品编号\n",
    "print(res1 / 10000)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "我们可以看到结果是0.8021，说明数据分布是符合预期的。\n",
    "\n",
    "我们可以多测几组数据，采用以下代码实现，其中用户的数量不对结果产生影响。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "(1) 20.0%的物品出现概率是其他物品的16.00倍\n",
      "> 物品数量400, 出售总数10,000, 畅销物品数量80, 预期销售占比 80.0%, 实际占比为 80.2%\n",
      "(2) 20.0%的物品出现概率是其他物品的36.00倍\n",
      "> 物品数量400, 出售总数10,000, 畅销物品数量80, 预期销售占比 90.0%, 实际占比为 89.6%\n",
      "(3) 10.0%的物品出现概率是其他物品的36.00倍\n",
      "> 物品数量800, 出售总数10,000, 畅销物品数量80, 预期销售占比 80.0%, 实际占比为 80.2%\n",
      "(4) 10.0%的物品出现概率是其他物品的1.00倍\n",
      "> 物品数量800, 出售总数10,000, 畅销物品数量80, 预期销售占比 10.0%, 实际占比为 10.3%\n"
     ]
    }
   ],
   "source": [
    "def data_test(user_nums = 200, item_nums = 400, N = 10000, item_p = 0.2, p = 0.8, seed = 1234):\n",
    "    data = generate_data(user_nums, item_nums, N, item_p, p, seed)\n",
    "    data_new = pd.DataFrame(data, columns = ['userid', 'itemid'])\n",
    "    cnt = Counter(data_new.itemid)\n",
    "    res1 = 0  # 存储前20%的物品出售数量\n",
    "    for i in cnt.keys():\n",
    "        res1 += cnt[i] if i < item_nums * item_p else 0  # 即前20%的物品编号\n",
    "    print('> 物品数量{:,}, 出售总数{:,}, 畅销物品数量{:,.0f}, 预期销售占比 {:.1%}, 实际占比为 {:.1%}'.format(item_nums, N, item_nums * item_p, p, res1 / N))\n",
    "    \n",
    "# 测试样例：用户数量，物品数量，记录数，畅销物品占比，畅销物品销售占比\n",
    "test_paramns = [[200, 400, 10000, 0.2, 0.8],\n",
    "                [200, 400, 10000, 0.2, 0.9],\n",
    "                [100, 800, 10000, 0.1, 0.8],\n",
    "                [100, 800, 10000, 0.1, 0.1]]\n",
    "for i, params in zip(range(len(test_paramns)), test_paramns):\n",
    "    user_nums, item_nums, N, item_p, p = params\n",
    "    print('(%d)'%(i+1), end = ' ')\n",
    "    data_test(user_nums, item_nums, N, item_p, p, seed = 1234)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 1.3 推荐效果\n",
    "\n",
    "#### 1.3.1 随机数据集检验推荐效果\n",
    "\n",
    "- 随机推荐\n",
    "- 热门推荐\n",
    "- 基于用户的协同过滤推荐"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "20.0%的物品出现概率是其他物品的16.00倍\n",
      "(10000, 2)\n"
     ]
    }
   ],
   "source": [
    "# 构造数据\n",
    "data = generate_data(user_nums = 200, item_nums = 400, N = 10000, item_p = 0.2, p = 0.8, seed = 1234)\n",
    "print(data.shape)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**(1)随机推荐**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Help on function my_test in module evaluate:\n",
      "\n",
      "my_test(data, recommend_func=<class 'recommend.RandomCF'>, N=20, M=8, desc=True, *args, **kwargs)\n",
      "    recommend_func: 推荐模块，取值可以为随机推荐，热门推荐，协同过滤等\n",
      "    K: 推荐个数\n",
      "    M: M折验证\n",
      "    desc: 显示每一轮的指标\n",
      "    args: 为了将结果导出\n",
      "    kwarsg: 兼容LFM隐语义模型\n",
      "\n"
     ]
    }
   ],
   "source": [
    "help(my_test)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "随机推荐20个物品\n",
      "epoch: 1, 召回率: 0.038397, 精确率: 0.011500, 覆盖率: 1.000000, 流行度: 2.343051 =============== 用时0.12s\n",
      "epoch: 2, 召回率: 0.059677, 精确率: 0.018500, 覆盖率: 1.000000, 流行度: 2.309284 =============== 用时0.08s\n",
      "epoch: 3, 召回率: 0.062972, 精确率: 0.018844, 覆盖率: 1.000000, 流行度: 2.321535 =============== 用时0.08s\n",
      "epoch: 4, 召回率: 0.060193, 精确率: 0.018750, 覆盖率: 1.000000, 流行度: 2.314299 =============== 用时0.10s\n",
      "epoch: 5, 召回率: 0.049414, 精确率: 0.014824, 覆盖率: 1.000000, 流行度: 2.346229 =============== 用时0.08s\n",
      "epoch: 6, 召回率: 0.043195, 精确率: 0.013317, 覆盖率: 1.000000, 流行度: 2.319412 =============== 用时0.10s\n",
      "epoch: 7, 召回率: 0.041906, 精确率: 0.012750, 覆盖率: 1.000000, 流行度: 2.331274 =============== 用时0.10s\n",
      "epoch: 8, 召回率: 0.043195, 精确率: 0.013317, 覆盖率: 1.000000, 流行度: 2.338503 =============== 用时0.08s\n",
      "\n",
      "\n",
      "\n",
      "[RandomCF] K:  20, 召回率: 0.049869, 精确率: 0.015225, 覆盖率: 1.000000, 流行度: 2.327948 =============== 用时0.75s\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "[[20, 0.04986866090367099, 0.015225188442211057, 1.0, 2.327948448661719]]"
      ]
     },
     "execution_count": 8,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "print(\"随机推荐20个物品\")\n",
    "res = []\n",
    "my_test(data, RandomCF, 20, 8, True, res)\n",
    "res"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "尝试不同的K值, 采用多线程方式实现。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "[RandomCF] K:  10, 召回率: 0.024940, 精确率: 0.038833, 覆盖率: 0.996641, 流行度: 2.110560 =============== 用时1.34s\n",
      "\n",
      "[RandomCF] K:  20, 召回率: 0.046830, 精确率: 0.036833, 覆盖率: 1.000000, 流行度: 2.124767 =============== 用时1.49s\n",
      "\n",
      "[RandomCF] K:   5, 召回率: 0.013076, 精确率: 0.041000, 覆盖率: 0.925868, 流行度: 2.115215 =============== 用时1.72s\n",
      "[RandomCF] K:  40, 召回率: 0.107384, 精确率: 0.041917, 覆盖率: 1.000000, 流行度: 2.105949 =============== 用时1.55s\n",
      "\n",
      "\n",
      "[RandomCF] K:  80, 召回率: 0.195038, 精确率: 0.038375, 覆盖率: 1.000000, 流行度: 2.098465 =============== 用时1.60s\n",
      "\n",
      "[RandomCF] K: 160, 召回率: 0.400702, 精确率: 0.038844, 覆盖率: 1.000000, 流行度: 2.111529 =============== 用时1.49s\n"
     ]
    }
   ],
   "source": [
    "test = Mythead()\n",
    "test.multi_test(data)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>K</th>\n",
       "      <th>召回率</th>\n",
       "      <th>准确率</th>\n",
       "      <th>覆盖率</th>\n",
       "      <th>流行度</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>5</td>\n",
       "      <td>1.31%</td>\n",
       "      <td>4.10%</td>\n",
       "      <td>92.59%</td>\n",
       "      <td>2.115215</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>10</td>\n",
       "      <td>2.49%</td>\n",
       "      <td>3.88%</td>\n",
       "      <td>99.66%</td>\n",
       "      <td>2.110560</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>20</td>\n",
       "      <td>4.68%</td>\n",
       "      <td>3.68%</td>\n",
       "      <td>100.00%</td>\n",
       "      <td>2.124767</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>40</td>\n",
       "      <td>10.74%</td>\n",
       "      <td>4.19%</td>\n",
       "      <td>100.00%</td>\n",
       "      <td>2.105949</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>80</td>\n",
       "      <td>19.50%</td>\n",
       "      <td>3.84%</td>\n",
       "      <td>100.00%</td>\n",
       "      <td>2.098465</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>5</th>\n",
       "      <td>160</td>\n",
       "      <td>40.07%</td>\n",
       "      <td>3.88%</td>\n",
       "      <td>100.00%</td>\n",
       "      <td>2.111529</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "     K     召回率    准确率      覆盖率       流行度\n",
       "0    5   1.31%  4.10%   92.59%  2.115215\n",
       "1   10   2.49%  3.88%   99.66%  2.110560\n",
       "2   20   4.68%  3.68%  100.00%  2.124767\n",
       "3   40  10.74%  4.19%  100.00%  2.105949\n",
       "4   80  19.50%  3.84%  100.00%  2.098465\n",
       "5  160  40.07%  3.88%  100.00%  2.111529"
      ]
     },
     "execution_count": 10,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "res = pd.DataFrame(test.res, columns = ['K', '召回率', '准确率', '覆盖率', '流行度'])\n",
    "res = res.sort_values('K', ascending = True).reset_index(drop = True)\n",
    "res[['召回率', '准确率', '覆盖率']] = res[['召回率', '准确率', '覆盖率']].applymap(lambda x:'{:.2%}'.format(x))\n",
    "res"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "从结果上来看，随机推荐的召回率随着K的提高不断提高，但准确率提高较少，另外覆盖率很高接近<font color = red>100%</font>。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**(2)热门推荐**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "[PopularityCF] K:   5, 召回率: 0.045481, 精确率: 0.140667, 覆盖率: 0.012584, 流行度: 4.406214 =============== 用时0.23s\n",
      "\n",
      "[PopularityCF] K:  10, 召回率: 0.094895, 精确率: 0.148167, 覆盖率: 0.025189, 流行度: 4.379655 =============== 用时0.35s\n",
      "\n",
      "[PopularityCF] K:  80, 召回率: 0.791254, 精确率: 0.153438, 覆盖率: 0.201852, 流行度: 4.211814 =============== 用时0.40s\n",
      "\n",
      "[PopularityCF] K:  20, 召回率: 0.186379, 精确率: 0.145333, 覆盖率: 0.050463, 流行度: 4.343218 =============== 用时0.84s\n",
      "\n",
      "[PopularityCF] K:  40, 召回率: 0.380410, 精确率: 0.148667, 覆盖率: 0.100926, 流行度: 4.304759 =============== 用时0.79s\n",
      "\n",
      "[PopularityCF] K: 160, 召回率: 0.843731, 精确率: 0.081667, 覆盖率: 0.404042, 流行度: 3.129335 =============== 用时0.67s\n"
     ]
    }
   ],
   "source": [
    "test = Mythead()\n",
    "test.multi_test(data, recommend_func=PopularityCF)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>K</th>\n",
       "      <th>召回率</th>\n",
       "      <th>准确率</th>\n",
       "      <th>覆盖率</th>\n",
       "      <th>流行度</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>5</td>\n",
       "      <td>4.55%</td>\n",
       "      <td>14.07%</td>\n",
       "      <td>1.26%</td>\n",
       "      <td>4.406214</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>10</td>\n",
       "      <td>9.49%</td>\n",
       "      <td>14.82%</td>\n",
       "      <td>2.52%</td>\n",
       "      <td>4.379655</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>20</td>\n",
       "      <td>18.64%</td>\n",
       "      <td>14.53%</td>\n",
       "      <td>5.05%</td>\n",
       "      <td>4.343218</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>40</td>\n",
       "      <td>38.04%</td>\n",
       "      <td>14.87%</td>\n",
       "      <td>10.09%</td>\n",
       "      <td>4.304759</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>80</td>\n",
       "      <td>79.13%</td>\n",
       "      <td>15.34%</td>\n",
       "      <td>20.19%</td>\n",
       "      <td>4.211814</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>5</th>\n",
       "      <td>160</td>\n",
       "      <td>84.37%</td>\n",
       "      <td>8.17%</td>\n",
       "      <td>40.40%</td>\n",
       "      <td>3.129335</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "     K     召回率     准确率     覆盖率       流行度\n",
       "0    5   4.55%  14.07%   1.26%  4.406214\n",
       "1   10   9.49%  14.82%   2.52%  4.379655\n",
       "2   20  18.64%  14.53%   5.05%  4.343218\n",
       "3   40  38.04%  14.87%  10.09%  4.304759\n",
       "4   80  79.13%  15.34%  20.19%  4.211814\n",
       "5  160  84.37%   8.17%  40.40%  3.129335"
      ]
     },
     "execution_count": 12,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "res = pd.DataFrame(test.res, columns = ['K', '召回率', '准确率', '覆盖率', '流行度'])\n",
    "res = res.sort_values('K', ascending = True).reset_index(drop = True)\n",
    "res[['召回率', '准确率', '覆盖率']] = res[['召回率', '准确率', '覆盖率']].applymap(lambda x:'{:.2%}'.format(x))\n",
    "res"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "从结果上来看，热门推荐的召回率随着K的提高不断提高，但准确率先上升后下降(**<font color = red>K=80</font>**是个较好的取值)，另外覆盖率相对于随机推荐降低了很多。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**(3)基于用户的协同过滤推荐**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "[UserCF] K:  20, 召回率: 0.192162, 精确率: 0.150583, 覆盖率: 0.201344, 流行度: 4.237991 =============== 用时25.49s\n",
      "\n",
      "[UserCF] K:  10, 召回率: 0.100316, 精确率: 0.157833, 覆盖率: 0.204208, 流行度: 4.232127 =============== 用时34.38s\n",
      "\n",
      "[UserCF] K:   5, 召回率: 0.051843, 精确率: 0.161667, 覆盖率: 0.216823, 流行度: 4.219925 =============== 用时38.30s\n",
      "\n",
      "[UserCF] K:  80, 召回率: 0.576653, 精确率: 0.112458, 覆盖率: 0.712498, 流行度: 3.587233 =============== 用时41.66s\n",
      "\n",
      "[UserCF] K:  40, 召回率: 0.389422, 精确率: 0.152250, 覆盖率: 0.201344, 流行度: 4.228461 =============== 用时41.93s\n",
      "\n",
      "[UserCF] K: 160, 召回率: 0.620487, 精确率: 0.060667, 覆盖率: 0.764904, 流行度: 2.744472 =============== 用时41.83s\n"
     ]
    }
   ],
   "source": [
    "test = Mythead()\n",
    "test.multi_test(data, recommend_func=UserCF)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>K</th>\n",
       "      <th>召回率</th>\n",
       "      <th>准确率</th>\n",
       "      <th>覆盖率</th>\n",
       "      <th>流行度</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>5</td>\n",
       "      <td>5.18%</td>\n",
       "      <td>16.17%</td>\n",
       "      <td>21.68%</td>\n",
       "      <td>4.219925</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>10</td>\n",
       "      <td>10.03%</td>\n",
       "      <td>15.78%</td>\n",
       "      <td>20.42%</td>\n",
       "      <td>4.232127</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>20</td>\n",
       "      <td>19.22%</td>\n",
       "      <td>15.06%</td>\n",
       "      <td>20.13%</td>\n",
       "      <td>4.237991</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>40</td>\n",
       "      <td>38.94%</td>\n",
       "      <td>15.22%</td>\n",
       "      <td>20.13%</td>\n",
       "      <td>4.228461</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>80</td>\n",
       "      <td>57.67%</td>\n",
       "      <td>11.25%</td>\n",
       "      <td>71.25%</td>\n",
       "      <td>3.587233</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>5</th>\n",
       "      <td>160</td>\n",
       "      <td>62.05%</td>\n",
       "      <td>6.07%</td>\n",
       "      <td>76.49%</td>\n",
       "      <td>2.744472</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "     K     召回率     准确率     覆盖率       流行度\n",
       "0    5   5.18%  16.17%  21.68%  4.219925\n",
       "1   10  10.03%  15.78%  20.42%  4.232127\n",
       "2   20  19.22%  15.06%  20.13%  4.237991\n",
       "3   40  38.94%  15.22%  20.13%  4.228461\n",
       "4   80  57.67%  11.25%  71.25%  3.587233\n",
       "5  160  62.05%   6.07%  76.49%  2.744472"
      ]
     },
     "execution_count": 14,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "res = pd.DataFrame(test.res, columns = ['K', '召回率', '准确率', '覆盖率', '流行度'])\n",
    "res = res.sort_values('K', ascending = True).reset_index(drop = True)\n",
    "res[['召回率', '准确率', '覆盖率']] = res[['召回率', '准确率', '覆盖率']].applymap(lambda x:'{:.2%}'.format(x))\n",
    "res"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "从结果上来看，基于用户的协同过滤推荐的召回率随着K的提高不断提高(到后面增幅下降)，但准确率先上升后下降(**<font color = red>K=80</font>**是个较好的取值)，另外覆盖率相对于热门推荐提高了很多，流行度有所下降说明推荐的物品不完全是热门产品。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 1.3.2 电影数据集检验推荐效果"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "【数据集大小】user_nums: 3734, movie_nums: 2312, N: 10146\n"
     ]
    }
   ],
   "source": [
    "# 加载全部的数据\n",
    "movie,_ = read_ratings(\"./data/ratings.dat\", pivot = 0.01)\n",
    "user_nums = len(set(u for u,_ in movie))\n",
    "movie_nums = len(set(m for _,m in movie))\n",
    "print('【数据集大小】user_nums: {}, movie_nums: {}, N: {}'.format(user_nums, movie_nums, len(movie)))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**(1)随机推荐**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "[RandomCF] K:   5, 召回率: 0.002448, 精确率: 0.000864, 覆盖率: 0.979024, 流行度: 1.276351 =============== 用时12.77s\n",
      "\n",
      "[RandomCF] K:  10, 召回率: 0.004182, 精确率: 0.000732, 覆盖率: 0.999010, 流行度: 1.277893 =============== 用时14.43s\n",
      "\n",
      "[RandomCF] K:  20, 召回率: 0.010118, 精确率: 0.000899, 覆盖率: 1.000000, 流行度: 1.268035 =============== 用时17.00s\n",
      "\n",
      "[RandomCF] K:  40, 召回率: 0.017730, 精确率: 0.000780, 覆盖率: 1.000000, 流行度: 1.276407 =============== 用时19.09s\n",
      "\n",
      "[RandomCF] K:  80, 召回率: 0.032978, 精确率: 0.000736, 覆盖率: 1.000000, 流行度: 1.277292 =============== 用时22.80s\n",
      "\n",
      "[RandomCF] K: 160, 召回率: 0.071177, 精确率: 0.000793, 覆盖率: 1.000000, 流行度: 1.271447 =============== 用时25.91s\n"
     ]
    }
   ],
   "source": [
    "test = Mythead()\n",
    "test.multi_test(movie, recommend_func=RandomCF)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>K</th>\n",
       "      <th>召回率</th>\n",
       "      <th>准确率</th>\n",
       "      <th>覆盖率</th>\n",
       "      <th>流行度</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>5</td>\n",
       "      <td>0.24%</td>\n",
       "      <td>0.09%</td>\n",
       "      <td>97.90%</td>\n",
       "      <td>1.276351</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>10</td>\n",
       "      <td>0.42%</td>\n",
       "      <td>0.07%</td>\n",
       "      <td>99.90%</td>\n",
       "      <td>1.277893</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>20</td>\n",
       "      <td>1.01%</td>\n",
       "      <td>0.09%</td>\n",
       "      <td>100.00%</td>\n",
       "      <td>1.268035</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>40</td>\n",
       "      <td>1.77%</td>\n",
       "      <td>0.08%</td>\n",
       "      <td>100.00%</td>\n",
       "      <td>1.276407</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>80</td>\n",
       "      <td>3.30%</td>\n",
       "      <td>0.07%</td>\n",
       "      <td>100.00%</td>\n",
       "      <td>1.277292</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>5</th>\n",
       "      <td>160</td>\n",
       "      <td>7.12%</td>\n",
       "      <td>0.08%</td>\n",
       "      <td>100.00%</td>\n",
       "      <td>1.271447</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "     K    召回率    准确率      覆盖率       流行度\n",
       "0    5  0.24%  0.09%   97.90%  1.276351\n",
       "1   10  0.42%  0.07%   99.90%  1.277893\n",
       "2   20  1.01%  0.09%  100.00%  1.268035\n",
       "3   40  1.77%  0.08%  100.00%  1.276407\n",
       "4   80  3.30%  0.07%  100.00%  1.277292\n",
       "5  160  7.12%  0.08%  100.00%  1.271447"
      ]
     },
     "execution_count": 17,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "res = pd.DataFrame(test.res, columns = ['K', '召回率', '准确率', '覆盖率', '流行度'])\n",
    "res = res.sort_values('K', ascending = True).reset_index(drop = True)\n",
    "res[['召回率', '准确率', '覆盖率']] = res[['召回率', '准确率', '覆盖率']].applymap(lambda x:'{:.2%}'.format(x))\n",
    "res"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**(2)热门推荐**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "[PopularityCF] K:   5, 召回率: 0.011813, 精确率: 0.004205, 覆盖率: 0.002903, 流行度: 3.184514 =============== 用时1.02s\n",
      "[PopularityCF] K:  10, 召回率: 0.023194, 精确率: 0.004146, 覆盖率: 0.005760, 流行度: 3.113752 =============== 用时1.02s\n",
      "[PopularityCF] K:  20, 召回率: 0.035671, 精确率: 0.003143, 覆盖率: 0.011542, 流行度: 3.017556 =============== 用时0.99s\n",
      "\n",
      "\n",
      "\n",
      "[PopularityCF] K:  40, 召回率: 0.072585, 精确率: 0.003220, 覆盖率: 0.023135, 流行度: 2.875308 =============== 用时1.68s\n",
      "\n",
      "[PopularityCF] K:  80, 召回率: 0.129995, 精确率: 0.002872, 覆盖率: 0.046133, 流行度: 2.733157 =============== 用时2.19s\n",
      "\n",
      "[PopularityCF] K: 160, 召回率: 0.216400, 精确率: 0.002396, 覆盖率: 0.092052, 流行度: 2.531088 =============== 用时2.77s\n"
     ]
    }
   ],
   "source": [
    "test = Mythead()\n",
    "test.multi_test(movie, recommend_func=PopularityCF)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>K</th>\n",
       "      <th>召回率</th>\n",
       "      <th>准确率</th>\n",
       "      <th>覆盖率</th>\n",
       "      <th>流行度</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>5</td>\n",
       "      <td>1.18%</td>\n",
       "      <td>0.42%</td>\n",
       "      <td>0.29%</td>\n",
       "      <td>3.184514</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>10</td>\n",
       "      <td>2.32%</td>\n",
       "      <td>0.41%</td>\n",
       "      <td>0.58%</td>\n",
       "      <td>3.113752</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>20</td>\n",
       "      <td>3.57%</td>\n",
       "      <td>0.31%</td>\n",
       "      <td>1.15%</td>\n",
       "      <td>3.017556</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>40</td>\n",
       "      <td>7.26%</td>\n",
       "      <td>0.32%</td>\n",
       "      <td>2.31%</td>\n",
       "      <td>2.875308</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>80</td>\n",
       "      <td>13.00%</td>\n",
       "      <td>0.29%</td>\n",
       "      <td>4.61%</td>\n",
       "      <td>2.733157</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>5</th>\n",
       "      <td>160</td>\n",
       "      <td>21.64%</td>\n",
       "      <td>0.24%</td>\n",
       "      <td>9.21%</td>\n",
       "      <td>2.531088</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "     K     召回率    准确率    覆盖率       流行度\n",
       "0    5   1.18%  0.42%  0.29%  3.184514\n",
       "1   10   2.32%  0.41%  0.58%  3.113752\n",
       "2   20   3.57%  0.31%  1.15%  3.017556\n",
       "3   40   7.26%  0.32%  2.31%  2.875308\n",
       "4   80  13.00%  0.29%  4.61%  2.733157\n",
       "5  160  21.64%  0.24%  9.21%  2.531088"
      ]
     },
     "execution_count": 19,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "res = pd.DataFrame(test.res, columns = ['K', '召回率', '准确率', '覆盖率', '流行度'])\n",
    "res = res.sort_values('K', ascending = True).reset_index(drop = True)\n",
    "res[['召回率', '准确率', '覆盖率']] = res[['召回率', '准确率', '覆盖率']].applymap(lambda x:'{:.2%}'.format(x))\n",
    "res"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**随机推荐和热门推荐的指标对比**\n",
    "\n",
    "为了反映该数据集上离线算法的基本性能，下表给出了两种基本推荐算法的性能。表中，Random算法每次都随机挑选**10**个用户没有产生过行为的物品推荐给当前用户，MostPopular算法则按照物品的流行度给用户推荐他没有产生过行为的物品中最热门的**10**个物品。这两种算法都是非个性化的推荐算法，但它们代表了两个极端。MostPopular算法的准确率和召回率远远高于Random算法，但它的覆盖率非常低，结果都非常热门。可见，Random算法的准确率和召回率很低，但覆盖度很高，结果平均流行度很低。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    " <table>\n",
    "    <tr>\n",
    "        <th></th>\n",
    "        <th>召回率</th>\n",
    "        <th>准确率</th>\n",
    "        <th>覆盖率</th>\n",
    "        <th>流行度</th>\n",
    "    </tr>\n",
    "    <tr>\n",
    "        <th>RandomCF</th>\n",
    "        <th>0.29%</th>\n",
    "        <th>1.59%</th>\n",
    "        <th>100.00%</th>\n",
    "        <th>4.183936</th>\n",
    "    </tr>\n",
    "    <tr>\n",
    "        <th>PopularityCF</th>\n",
    "        <th>2.76%</th>\n",
    "        <th>15.25%</th>\n",
    "        <th>0.27%</th>\n",
    "        <th>7.532863</th>\n",
    "    </tr>\n",
    "</table>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**(3)基于用户的协同过滤推荐**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "[UserCF] K:  80, 召回率: 0.103437, 精确率: 0.002294, 覆盖率: 0.956696, 流行度: 2.393539 =============== 用时132.29s\n",
      "\n",
      "[UserCF] K:   5, 召回率: 0.008307, 精确率: 0.002945, 覆盖率: 0.667463, 流行度: 2.539483 =============== 用时164.37s\n",
      "\n",
      "[UserCF] K:  10, 召回率: 0.013028, 精确率: 0.002311, 覆盖率: 0.867253, 流行度: 2.273140 =============== 用时195.73s\n",
      "\n",
      "[UserCF] K: 160, 召回率: 0.196023, 精确率: 0.002174, 覆盖率: 0.956696, 流行度: 2.412754 =============== 用时202.15s\n",
      "\n",
      "[UserCF] K:  20, 召回率: 0.020816, 精确率: 0.001847, 覆盖率: 0.945249, 流行度: 2.143503 =============== 用时212.29s\n",
      "\n",
      "[UserCF] K:  40, 召回率: 0.048912, 精确率: 0.002170, 覆盖率: 0.956171, 流行度: 2.239866 =============== 用时212.67s\n"
     ]
    }
   ],
   "source": [
    "test = Mythead()\n",
    "test.multi_test(movie, recommend_func=UserCF)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>K</th>\n",
       "      <th>召回率</th>\n",
       "      <th>准确率</th>\n",
       "      <th>覆盖率</th>\n",
       "      <th>流行度</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>5</td>\n",
       "      <td>0.83%</td>\n",
       "      <td>0.29%</td>\n",
       "      <td>66.75%</td>\n",
       "      <td>2.539483</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>10</td>\n",
       "      <td>1.30%</td>\n",
       "      <td>0.23%</td>\n",
       "      <td>86.73%</td>\n",
       "      <td>2.273140</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>20</td>\n",
       "      <td>2.08%</td>\n",
       "      <td>0.18%</td>\n",
       "      <td>94.52%</td>\n",
       "      <td>2.143503</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>40</td>\n",
       "      <td>4.89%</td>\n",
       "      <td>0.22%</td>\n",
       "      <td>95.62%</td>\n",
       "      <td>2.239866</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>80</td>\n",
       "      <td>10.34%</td>\n",
       "      <td>0.23%</td>\n",
       "      <td>95.67%</td>\n",
       "      <td>2.393539</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>5</th>\n",
       "      <td>160</td>\n",
       "      <td>19.60%</td>\n",
       "      <td>0.22%</td>\n",
       "      <td>95.67%</td>\n",
       "      <td>2.412754</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "     K     召回率    准确率     覆盖率       流行度\n",
       "0    5   0.83%  0.29%  66.75%  2.539483\n",
       "1   10   1.30%  0.23%  86.73%  2.273140\n",
       "2   20   2.08%  0.18%  94.52%  2.143503\n",
       "3   40   4.89%  0.22%  95.62%  2.239866\n",
       "4   80  10.34%  0.23%  95.67%  2.393539\n",
       "5  160  19.60%  0.22%  95.67%  2.412754"
      ]
     },
     "execution_count": 21,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "res = pd.DataFrame(test.res, columns = ['K', '召回率', '准确率', '覆盖率', '流行度'])\n",
    "res = res.sort_values('K', ascending = True).reset_index(drop = True)\n",
    "res[['召回率', '准确率', '覆盖率']] = res[['召回率', '准确率', '覆盖率']].applymap(lambda x:'{:.2%}'.format(x))\n",
    "res"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 优化算法"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "[UserCFIUF] K:  40, 召回率: 0.048074, 精确率: 0.002126, 覆盖率: 0.958222, 流行度: 2.234476 =============== 用时185.75s\n",
      "\n",
      "[UserCFIUF] K:  20, 召回率: 0.019345, 精确率: 0.001718, 覆盖率: 0.952570, 流行度: 2.114772 =============== 用时231.54s\n",
      "\n",
      "[UserCFIUF] K:  10, 召回率: 0.011655, 精确率: 0.002071, 覆盖率: 0.901954, 流行度: 2.195152 =============== 用时234.16s\n",
      "\n",
      "[UserCFIUF] K: 160, 召回率: 0.197105, 精确率: 0.002184, 覆盖率: 0.955210, 流行度: 2.416788 =============== 用时237.28s\n",
      "\n",
      "[UserCFIUF] K:   5, 召回率: 0.007942, 精确率: 0.002823, 覆盖率: 0.740428, 流行度: 2.401619 =============== 用时245.28s\n",
      "\n",
      "[UserCFIUF] K:  80, 召回率: 0.106407, 精确率: 0.002337, 覆盖率: 0.959906, 流行度: 2.379362 =============== 用时245.55s\n"
     ]
    }
   ],
   "source": [
    "# 优化的基于物品的协同过滤\n",
    "test = Mythead()\n",
    "test.multi_test(movie, UserCFIUF)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>K</th>\n",
       "      <th>召回率</th>\n",
       "      <th>准确率</th>\n",
       "      <th>覆盖率</th>\n",
       "      <th>流行度</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>5</td>\n",
       "      <td>0.79%</td>\n",
       "      <td>0.28%</td>\n",
       "      <td>74.04%</td>\n",
       "      <td>2.401619</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>10</td>\n",
       "      <td>1.17%</td>\n",
       "      <td>0.21%</td>\n",
       "      <td>90.20%</td>\n",
       "      <td>2.195152</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>20</td>\n",
       "      <td>1.93%</td>\n",
       "      <td>0.17%</td>\n",
       "      <td>95.26%</td>\n",
       "      <td>2.114772</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>40</td>\n",
       "      <td>4.81%</td>\n",
       "      <td>0.21%</td>\n",
       "      <td>95.82%</td>\n",
       "      <td>2.234476</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>80</td>\n",
       "      <td>10.64%</td>\n",
       "      <td>0.23%</td>\n",
       "      <td>95.99%</td>\n",
       "      <td>2.379362</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>5</th>\n",
       "      <td>160</td>\n",
       "      <td>19.71%</td>\n",
       "      <td>0.22%</td>\n",
       "      <td>95.52%</td>\n",
       "      <td>2.416788</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "     K     召回率    准确率     覆盖率       流行度\n",
       "0    5   0.79%  0.28%  74.04%  2.401619\n",
       "1   10   1.17%  0.21%  90.20%  2.195152\n",
       "2   20   1.93%  0.17%  95.26%  2.114772\n",
       "3   40   4.81%  0.21%  95.82%  2.234476\n",
       "4   80  10.64%  0.23%  95.99%  2.379362\n",
       "5  160  19.71%  0.22%  95.52%  2.416788"
      ]
     },
     "execution_count": 23,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "res = pd.DataFrame(test.res, columns = ['K', '召回率', '准确率', '覆盖率', '流行度'])\n",
    "res = res.sort_values('K', ascending = True).reset_index(drop = True)\n",
    "res[['召回率', '准确率', '覆盖率']] = res[['召回率', '准确率', '覆盖率']].applymap(lambda x:'{:.2%}'.format(x))\n",
    "res"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "[ItemCF] K:   5, 召回率: 0.007639, 精确率: 0.002718, 覆盖率: 0.445613, 流行度: 2.511060 =============== 用时100.89s\n",
      "\n",
      "[ItemCF] K:  80, 召回率: 0.115456, 精确率: 0.002560, 覆盖率: 0.752572, 流行度: 2.431139 =============== 用时104.16s\n",
      "\n",
      "[ItemCF] K:  10, 召回率: 0.015999, 精确率: 0.002830, 覆盖率: 0.589487, 流行度: 2.374502 =============== 用时113.29s\n",
      "\n",
      "[ItemCF] K: 160, 召回率: 0.203535, 精确率: 0.002260, 覆盖率: 0.765266, 流行度: 2.382863 =============== 用时118.19s\n",
      "\n",
      "[ItemCF] K:  40, 召回率: 0.056462, 精确率: 0.002521, 覆盖率: 0.732937, 流行度: 2.390006 =============== 用时120.93s\n",
      "\n",
      "[ItemCF] K:  20, 召回率: 0.028537, 精确率: 0.002517, 覆盖率: 0.687626, 流行度: 2.350743 =============== 用时121.71s\n"
     ]
    }
   ],
   "source": [
    "# 基于物品的算法\n",
    "test = Mythead()\n",
    "test.multi_test(movie, ItemCF)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>K</th>\n",
       "      <th>召回率</th>\n",
       "      <th>准确率</th>\n",
       "      <th>覆盖率</th>\n",
       "      <th>流行度</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>5</td>\n",
       "      <td>0.76%</td>\n",
       "      <td>0.27%</td>\n",
       "      <td>44.56%</td>\n",
       "      <td>2.511060</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>10</td>\n",
       "      <td>1.60%</td>\n",
       "      <td>0.28%</td>\n",
       "      <td>58.95%</td>\n",
       "      <td>2.374502</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>20</td>\n",
       "      <td>2.85%</td>\n",
       "      <td>0.25%</td>\n",
       "      <td>68.76%</td>\n",
       "      <td>2.350743</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>40</td>\n",
       "      <td>5.65%</td>\n",
       "      <td>0.25%</td>\n",
       "      <td>73.29%</td>\n",
       "      <td>2.390006</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>80</td>\n",
       "      <td>11.55%</td>\n",
       "      <td>0.26%</td>\n",
       "      <td>75.26%</td>\n",
       "      <td>2.431139</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>5</th>\n",
       "      <td>160</td>\n",
       "      <td>20.35%</td>\n",
       "      <td>0.23%</td>\n",
       "      <td>76.53%</td>\n",
       "      <td>2.382863</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "     K     召回率    准确率     覆盖率       流行度\n",
       "0    5   0.76%  0.27%  44.56%  2.511060\n",
       "1   10   1.60%  0.28%  58.95%  2.374502\n",
       "2   20   2.85%  0.25%  68.76%  2.350743\n",
       "3   40   5.65%  0.25%  73.29%  2.390006\n",
       "4   80  11.55%  0.26%  75.26%  2.431139\n",
       "5  160  20.35%  0.23%  76.53%  2.382863"
      ]
     },
     "execution_count": 25,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "res = pd.DataFrame(test.res, columns = ['K', '召回率', '准确率', '覆盖率', '流行度'])\n",
    "res = res.sort_values('K', ascending = True).reset_index(drop = True)\n",
    "res[['召回率', '准确率', '覆盖率']] = res[['召回率', '准确率', '覆盖率']].applymap(lambda x:'{:.2%}'.format(x))\n",
    "res"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "[ItemCFNorm] K:  20, 召回率: 0.029482, 精确率: 0.002599, 覆盖率: 0.685079, 流行度: 2.350748 =============== 用时79.63s\n",
      "\n",
      "[ItemCFNorm] K:  40, 召回率: 0.054133, 精确率: 0.002406, 覆盖率: 0.731404, 流行度: 2.387156 =============== 用时84.55s\n",
      "\n",
      "[ItemCFNorm] K:  10, 召回率: 0.014847, 精确率: 0.002648, 覆盖率: 0.589063, 流行度: 2.394494 =============== 用时104.60s\n",
      "\n",
      "[ItemCFNorm] K:   5, 召回率: 0.008296, 精确率: 0.002957, 覆盖率: 0.440009, 流行度: 2.498916 =============== 用时111.12s\n",
      "\n",
      "[ItemCFNorm] K:  80, 召回率: 0.111218, 精确率: 0.002465, 覆盖率: 0.753281, 流行度: 2.430130 =============== 用时111.17s\n",
      "\n",
      "[ItemCFNorm] K: 160, 召回率: 0.202289, 精确率: 0.002240, 覆盖率: 0.765482, 流行度: 2.385076 =============== 用时111.33s\n"
     ]
    }
   ],
   "source": [
    "# 归一化\n",
    "test = Mythead()\n",
    "test.multi_test(movie, ItemCFNorm)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>K</th>\n",
       "      <th>召回率</th>\n",
       "      <th>准确率</th>\n",
       "      <th>覆盖率</th>\n",
       "      <th>流行度</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>5</td>\n",
       "      <td>0.83%</td>\n",
       "      <td>0.30%</td>\n",
       "      <td>44.00%</td>\n",
       "      <td>2.498916</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>10</td>\n",
       "      <td>1.48%</td>\n",
       "      <td>0.26%</td>\n",
       "      <td>58.91%</td>\n",
       "      <td>2.394494</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>20</td>\n",
       "      <td>2.95%</td>\n",
       "      <td>0.26%</td>\n",
       "      <td>68.51%</td>\n",
       "      <td>2.350748</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>40</td>\n",
       "      <td>5.41%</td>\n",
       "      <td>0.24%</td>\n",
       "      <td>73.14%</td>\n",
       "      <td>2.387156</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>80</td>\n",
       "      <td>11.12%</td>\n",
       "      <td>0.25%</td>\n",
       "      <td>75.33%</td>\n",
       "      <td>2.430130</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>5</th>\n",
       "      <td>160</td>\n",
       "      <td>20.23%</td>\n",
       "      <td>0.22%</td>\n",
       "      <td>76.55%</td>\n",
       "      <td>2.385076</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "     K     召回率    准确率     覆盖率       流行度\n",
       "0    5   0.83%  0.30%  44.00%  2.498916\n",
       "1   10   1.48%  0.26%  58.91%  2.394494\n",
       "2   20   2.95%  0.26%  68.51%  2.350748\n",
       "3   40   5.41%  0.24%  73.14%  2.387156\n",
       "4   80  11.12%  0.25%  75.33%  2.430130\n",
       "5  160  20.23%  0.22%  76.55%  2.385076"
      ]
     },
     "execution_count": 27,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "res = pd.DataFrame(test.res, columns = ['K', '召回率', '准确率', '覆盖率', '流行度'])\n",
    "res = res.sort_values('K', ascending = True).reset_index(drop = True)\n",
    "res[['召回率', '准确率', '覆盖率']] = res[['召回率', '准确率', '覆盖率']].applymap(lambda x:'{:.2%}'.format(x))\n",
    "res"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "[ItemCF] K:  80, 召回率: 0.106561, 精确率: 0.001699, 覆盖率: 0.811650, 流行度: 2.413002 =============== 用时50.70s\n"
     ]
    }
   ],
   "source": [
    "# 哈利波特问题\n",
    "# 以 N=80 举例\n",
    "my_test(movie, ItemCF, 80, 8, False, [])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 29,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "alpha: 0.40 \n",
      "[ItemCF_alpha] K:  80, 召回率: 0.116848, 精确率: 0.002592, 覆盖率: 0.752518, 流行度: 2.419638 =============== 用时19.19s\n",
      "alpha: 0.50 \n",
      "[ItemCF_alpha] K:  80, 召回率: 0.116848, 精确率: 0.002592, 覆盖率: 0.752518, 流行度: 2.419638 =============== 用时20.41s\n",
      "alpha: 0.55 \n",
      "[ItemCF_alpha] K:  80, 召回率: 0.116848, 精确率: 0.002592, 覆盖率: 0.752518, 流行度: 2.419638 =============== 用时19.98s\n",
      "alpha: 0.60 \n",
      "[ItemCF_alpha] K:  80, 召回率: 0.116848, 精确率: 0.002592, 覆盖率: 0.752518, 流行度: 2.419638 =============== 用时19.66s\n",
      "alpha: 0.70 \n",
      "[ItemCF_alpha] K:  80, 召回率: 0.116848, 精确率: 0.002592, 覆盖率: 0.752518, 流行度: 2.419638 =============== 用时19.25s\n"
     ]
    }
   ],
   "source": [
    "for alpha in [0.4,0.5,0.55,0.6,0.7]:\n",
    "    print('alpha: {:.2f}'.format(alpha), end = ' ')\n",
    "    my_test(movie, ItemCF_alpha, 80, 3, False, [], alpha = alpha)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 30,
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "[LatenFactorModel] K:   5, 召回率: 0.002445, 精确率: 0.007667, 覆盖率: 0.753962, 流行度: 1.549446 =============== 用时35.68s\n",
      "\n",
      "[LatenFactorModel] K:  10, 召回率: 0.006306, 精确率: 0.009833, 覆盖率: 0.798333, 流行度: 1.554199 =============== 用时36.19s\n",
      "\n",
      "[LatenFactorModel] K:  20, 召回率: 0.013570, 精确率: 0.010583, 覆盖率: 0.821667, 流行度: 1.556226 =============== 用时38.73s\n",
      "\n",
      "[LatenFactorModel] K:  80, 召回率: 0.066880, 精确率: 0.013062, 覆盖率: 0.992500, 流行度: 1.606309 =============== 用时45.35s\n",
      "\n",
      "[LatenFactorModel] K:  40, 召回率: 0.026946, 精确率: 0.010500, 覆盖率: 0.888333, 流行度: 1.565924 =============== 用时46.28s\n",
      "\n",
      "[LatenFactorModel] K: 160, 召回率: 0.185602, 精确率: 0.018073, 覆盖率: 1.000000, 流行度: 1.696837 =============== 用时46.39s\n"
     ]
    }
   ],
   "source": [
    "# 隐语义模型测试\n",
    "user_nums, item_nums, F = len(set(data[:,0])), len(set(data[:, 1])), 100\n",
    "# 热门产品\n",
    "item_count = Counter(data[:,1])\n",
    "items_pool = sorted(item_count.items(), key = lambda x:x[1], reverse = True)[:len(item_count)//5]\n",
    "items_pool = [i for i,_ in items_pool]\n",
    "\n",
    "\n",
    "test = Mythead()\n",
    "test.multi_test(data, LatenFactorModel, user_nums = user_nums, item_nums = item_nums, F = F, max_iters = 10\n",
    "        ,alpha_ = 0.02, lambda_ = 0.01, radio = 1, items_pool = items_pool)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.8.5"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
